package com.raise.drive.commons;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.File;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.Collection;
import java.util.Date;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Pattern;

/**
 * @author Eugene Rozov
 */
public abstract class Utils {

    public enum EmailStatus {
        VALID,
        EMPTY,
        WRONG_SYNTAX,
        WRONG_DNS
    }

    private static final Log logger = LogFactory.getLog(Utils.class);

    /**
     * Check if string has nonzero length
     *
     * @param test a string to test
     * @return false if string is null or has a zero length
     */
    public static boolean hasLength(String test) {
        return test != null && test.length() > 0;
    }

    /**
     * Check if string has nonzero length
     *
     * @param test a array of objects to test
     * @return false if array is null or has a zero length
     */
    public static boolean hasLength(Object[] test) {
        return test != null && test.length > 0;
    }

    /**
     * Check if collection has nonzero length
     *
     * @param test a collection to test
     * @return false if collection is null or has a zero length
     */
    public static boolean hasLength(Collection<?> test) {
        return test != null && !test.isEmpty();
    }

    /**
     * Check if list has nonzero length
     *
     * @param test a map to test
     * @return false if map is null or has a zero length
     */
    public static boolean hasLength(Map<?, ?> test) {
        return test != null && !test.isEmpty();
    }

    /**
     * Check is the string is not empty and has at least one non whitespace symbol
     *
     * @param str a given string
     * @return true is string is not empty
     */
    public static boolean hasText(String str) {
        if (!hasLength(str)) {
            return false;
        }
        int strLen = str.length();
        for (int i = 0; i < strLen; i++) {
            if (!Character.isWhitespace(str.charAt(i))) {
                return true;
            }
        }
        return false;
    }

    /**
     * Verify if an e-mail address is valid
     *
     * @param mail e-mail address for verifying
     * @return status of validation
     */
    public static EmailStatus validateMail(String mail) {
        if (!hasLength(mail)) {
            return EmailStatus.EMPTY;
        }

        // check with regular expression
        try {
            String mask = "^\\w+((-\\w+)|(\\.\\w+))*\\@[A-Za-z0-9]+((\\.|-)[A-Za-z0-9]+)*\\.[A-Za-z0-9]+$";
            Pattern pattern = Pattern.compile(mask);
            if (!pattern.matcher(mail.trim()).matches()) {
                return EmailStatus.WRONG_SYNTAX;
            }
        } catch (Exception e) {
            logger.error(e);
        }
        return EmailStatus.VALID;
    }

    /**
     * Check if password is valid (the minimum requirement should be 6 characters and at least 1 numerical and 1 alphabetical character)
     *
     * @param password a entered password
     * @return true if password is valid
     */
    public static boolean isPasswordValid(String password) {
        return password.matches("^(?=.*[\\p{Alpha}])(?=.*[\\p{Digit}]).{6,}$");
    }

    /**
     * Perform a round of double value
     *
     * @param value double value
     * @param scale scale of the value to be returned
     * @return rounded double value
     */
    public static double roundDoubleValue(double value, int scale) {
        BigDecimal d = new BigDecimal(value);
        return d.setScale(scale, RoundingMode.HALF_UP).doubleValue();
    }

    /**
     * Perform residue of division on double values
     * Operator '%' not always works correctly on double operands (like 9.95 % 0.05 not always equals to 0)
     *
     * @param dividend a first operator-divident
     * @param divisor  a second operator-divisor
     * @return residue of division
     */
    public static double mod(double dividend, double divisor) {
        return Math.abs(roundDoubleValue(dividend % divisor, 12)) % Math.abs(divisor);
    }

    public static String formatInteger(int value, Locale locale) {
        NumberFormat format = DecimalFormat.getInstance(locale);
        format.setParseIntegerOnly(true);
        format.setMaximumFractionDigits(0);
        return format.format(value);
    }

    public static String formatInteger(double value, Locale locale) {
        NumberFormat format = DecimalFormat.getInstance(locale);
        format.setParseIntegerOnly(true);
        format.setMaximumFractionDigits(0);
        return format.format(value);
    }

    public static String formatDouble(double value, int minFractionDigits, int maxFractionDigits, Locale locale) {
        NumberFormat format = DecimalFormat.getInstance(locale);
        format.setMinimumFractionDigits(minFractionDigits);
        format.setMaximumFractionDigits(maxFractionDigits);
        return format.format(value);
    }

    public static String formatDate(Date date, Locale locale) {
        DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, locale);
        return formatter.format(date);
    }

    /**
     * Separate file name from it extention. Source name "ReadMe.txt" will return String[] {"ReadMe", ".txt"}
     *
     * @param fileName a name of file
     * @return array. First element - file name w/o extention; second element - extention with a dot
     */
    public static String[] separateNameExtention(String fileName) {
        int index = fileName.lastIndexOf('.');
        if (index == -1) {
            return new String[]{fileName, ""};
        }
        String cleanFileName = fileName.substring(0, index);
        String extention = fileName.substring(index);
        return new String[]{cleanFileName, extention};
    }

    /**
     * Get free file name, based on source name. If file with source name don't exist, it be returned. Else method
     * returns new file name in pattern "sourceFileName_{X}", where X starts from 0.
     *
     * @param path     a path to checking directory
     * @param fileName a checking file name
     * @param compareWithPath
     * @return new file name based on source
     */
    public static String getFreeFileName(String path, String fileName, boolean compareWithPath) {
        String[] separatedFileName = Utils.separateNameExtention(fileName);
        File newFile = new File(path + fileName);
        int count = 1;
        String newFileName = fileName;
        while (newFile.exists()) {
            newFileName = separatedFileName[0] + "_(" + count + ")" + separatedFileName[1];
            newFile = new File(path + newFileName);
            count++;
        }

        if (compareWithPath) {
            return path + newFileName;
        }
        return newFileName;
    }

}
